package org.cogaen.java2d.xml;

import java.io.IOException;
import java.io.InputStream;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.cogaen.core.Core;
import org.cogaen.java2d.AnimationHandle;
import org.cogaen.java2d.ColorHandle;
import org.cogaen.java2d.FontHandle;
import org.cogaen.java2d.ImageHandle;
import org.cogaen.java2d.SpriteHandle;
import org.cogaen.java2d.TextHandle;
import org.cogaen.resource.ResourceManager;

public class GraphicsResourceUtil {

	private static final String GROUP_ELEMENT = "group";
	private static final String NAME_SPACE = null;
	private static final String ID_ATTRIB = "id";
	private XMLInputFactory factory;
	private ResourceManager resMngr;
	private boolean referenceMode = true;
	
	
	public GraphicsResourceUtil(Core core) {
		this.resMngr = ResourceManager.getInstance(core);
		this.factory = XMLInputFactory.newFactory();
	}

	public void registerResources(String filename, String group) throws ParseException {
		InputStream is = this.resMngr.getStream(filename);
		
		if (is == null) {
			throw new ParseException("resource configuration file '" + filename + "' not found");
		} else {
			parseXml(is, filename, group);
			try {
				is.close();
			} catch (IOException e) {
				throw new ParseException("unable to close resource configuration file '" + filename + "'", e);
			}
		}

	}
	
	public void registerResources(String filename) throws ParseException {
		registerResources(filename, null);
	}

	private void parseXml(InputStream is, String filename, String group) throws ParseException {
		XMLStreamReader reader = null;
		
		try {
			reader = this.factory.createFilteredReader(this.factory.createXMLStreamReader(is), new ElementFilter(GROUP_ELEMENT, group));			
			parseXml(reader, filename);
		} catch (XMLStreamException e) {
			throw new ParseException("error reading resource configuration file '" + filename + "'", e);
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (XMLStreamException e) {
					throw new ParseException("error parsing resource configuration file '" + filename + "'", e);
				}
			}			
		}
	}

	private void parseXml(XMLStreamReader reader, String filename) throws XMLStreamException, ParseException {
		String group = null;
		
		while (reader.hasNext()) {
			int event = reader.getEventType();
			
			switch (event) {
			case XMLStreamConstants.START_ELEMENT:
				if (GROUP_ELEMENT.equals(reader.getLocalName())) {
					group = reader.getAttributeValue(NAME_SPACE, ID_ATTRIB);
					if (group == null) {
						group = ResourceManager.DEFAULT_GROUP;
					}
				} else {
					if (isReferenceMode()) {
						addRefResourceHandle(reader, group, filename);						
					} else {
						addResourceHandle(reader, group, filename);												
					}
				}
				break;
				
			case XMLStreamConstants.END_ELEMENT:
				if (GROUP_ELEMENT.equals(reader.getLocalName())) {
					group = null;
				}
				break;				
			}			
			
			reader.next();
		}
	}

	private void addRefResourceHandle(XMLStreamReader reader, String group, String filename) throws ParseException {
		String id = reader.getAttributeValue(NAME_SPACE, ID_ATTRIB);
		
		if (id == null) {
			throw new ParseException("missing resource id in xml element + '" + reader.getLocalName() + "' in '" + filename + "'");
		}
		
		if (TextParser.isStart(reader)) {
			this.resMngr.addResource(group, new TextXmlHandle(id, filename));
		} else if (FontParser.isStart(reader)) {
			this.resMngr.addResource(group, new FontXmlHandle(id, filename));
		} else if (ColorParser.isStart(reader)) {
			this.resMngr.addResource(group, new ColorXmlHandle(id, filename));
		} else if (ImageParser.isStart(reader)) {
			this.resMngr.addResource(group, new ImageXmlHandle(id, filename));
		} else if (SpriteParser.isStart(reader)) {
			this.resMngr.addResource(group, new SpriteXmlHandle(id, filename));
		} else if (AnimationParser.isStart(reader)) {
			this.resMngr.addResource(group, new AnimationXmlHandle(id, filename));			
		}
	}

	private void addResourceHandle(XMLStreamReader reader, String group, String filename) throws ParseException, XMLStreamException {
		String id = reader.getAttributeValue(NAME_SPACE, ID_ATTRIB);
		
		if (id == null) {
			throw new ParseException("missing resource id in xml element + '" + reader.getLocalName() + "' in '" + filename + "'");
		}
		
		if (TextParser.isStart(reader)) {
			TextParser textParser = new TextParser();
			textParser.parse(reader);
			this.resMngr.addResource(group, new TextHandle(id, textParser.getText(), textParser.getFontName(), textParser.getColorName(), textParser.getOffsetX(), textParser.getOffsetY()));
		} else if (FontParser.isStart(reader)) {
			FontParser fontParser = new FontParser();
			fontParser.parse(reader);
			this.resMngr.addResource(group, new FontHandle(id, fontParser.getName(), fontParser.getStyle(), fontParser.getSize()));
		} else if (ColorParser.isStart(reader)) {
			ColorParser colorParser = new ColorParser();
			colorParser.parse(reader);
			this.resMngr.addResource(group, new ColorHandle(id, colorParser.getColor()));
		} else if (ImageParser.isStart(reader)) {
			ImageParser imageParser = new ImageParser();
			imageParser.parse(reader);
			this.resMngr.addResource(group, new ImageHandle(id, imageParser.getImageSrc()));
		} else if (SpriteParser.isStart(reader)) {
			SpriteParser spriteParser = new SpriteParser();
			spriteParser.parse(reader);
			this.resMngr.addResource(group, new SpriteHandle(id, spriteParser.getImageName(), spriteParser.getWidth(), spriteParser.getHeight()));
		} else if (AnimationParser.isStart(reader)) {
			AnimationParser animationParser = new AnimationParser();
			animationParser.parse(reader);
			AnimationHandle handle = new AnimationHandle(id, animationParser.getImageName(), animationParser.getColumns(), animationParser.getRows(), animationParser.getWidth(), animationParser.getHeight());
			handle.setDisplayTime(animationParser.getDisplayTime());
			handle.setFirstFrame(animationParser.getFirstFrame());
			handle.setNumFrames(animationParser.getNumFrames());
			this.resMngr.addResource(group, handle);
		}
	}
	
	
	public boolean isReferenceMode() {
		return referenceMode;
	}

	public void setReferenceMode(boolean referenceMode) {
		this.referenceMode = referenceMode;
	}
}
